package mlp.game.shadownbolts;


import android.content.Context;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.graphics.Paint.Align;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;

public class RenderView extends SurfaceView implements SurfaceHolder.Callback {
	public static final int KeyEvent_KEYCODE_BUTTON_START = 108; // Added on API 9
	
	public static final String TYPEFACE_PATH = "fonts/celestiaredux.ttf";
	public static final boolean SHOW_DEBUG_INFO = false;

	public static final long CONFIG_FRAME_DELAY = 1000 / 30;
	
	public int SCORE = 0;

	public static float SCALE = 1;

	private SurfaceHolder surfaceHolder;
	public static Rect visibleScreenArea = null;
	private RenderThread renderThread;
	
	private Spitfire player;
	private Shadowbolt enemy;
	private Projectile projectile;
	private ProjectileCollector projectileCollector;
	
	private Cloud cloudTop;
	private Cloud cloudBottom;
	
	private Renderable btnAttack;
	private Renderable btnShield;
	
	public boolean initialized = false;
	public boolean gameRunning = false;
	public boolean gamePaused = false;

	Paint scoreText = new Paint();
	Paint scoreBoardFill = new Paint();
	Paint scoreBoardStroke = new Paint();
	Paint pauseText = new Paint();

	int backgroundColor = 0xFF00002d;
	int countdownLeft = 0;

	private GameOverListener gameOverListener;

	long gameStartTime;
	long gameRunTime;
	long gamePauseTime;
	long nextFlashTime = 0;
	
	boolean flash = false;

	public RenderView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		this.surfaceHolder = getHolder();
		this.surfaceHolder.addCallback(this);
	}

	public RenderView(Context context, AttributeSet attrs) {
		super(context, attrs);
		this.surfaceHolder = getHolder();
		this.surfaceHolder.addCallback(this);
	}

	public RenderView(Context context) {
		super(context);
		this.surfaceHolder = getHolder();
		this.surfaceHolder.addCallback(this);
	}

	public void initialize() {
		this.renderThread = new RenderThread(this);

		DisplayMetrics metrics = new DisplayMetrics();
		((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getMetrics(metrics);
		SCALE = metrics.density;

		Sprite.debugPaint = new Paint();
		Sprite.debugPaint.setColor(Color.RED);
		Sprite.debugPaint.setStyle(Style.STROKE);
		
		// Set size of your gameView
		visibleScreenArea = surfaceHolder.getSurfaceFrame();
		
		cloudTop = new Cloud(new Sprite(BitmapFactory.decodeResource(getResources(), R.drawable.cloud_top), 24, 1), false, 0);
		cloudBottom = new Cloud(new Sprite(BitmapFactory.decodeResource(getResources(), R.drawable.cloud_bot), 24, 1), true, cloudTop.getWidth());
		
		player = new Spitfire(new Sprite(BitmapFactory.decodeResource(getResources(), R.drawable.spitfire_sprite), 30, 29), 30, new Sprite(BitmapFactory.decodeResource(getResources(), R.drawable.arrow), 60, 11, false));
		player.movePerKey = player.movePerKey * SCALE;
		
		enemy = new Shadowbolt(new Sprite(BitmapFactory.decodeResource(getResources(), R.drawable.shadowbolt), 30, 9));
		enemy.spawn();
		
		projectile = new Projectile(new Sprite(BitmapFactory.decodeResource(getResources(), R.drawable.dot), 24, 1));
				
		projectileCollector = new ProjectileCollector(player);
		
		projectile.player = this.player;
		projectile.enemy = this.enemy;
		projectile.target = this.projectileCollector;
		
		btnShield = new Renderable(new Sprite(BitmapFactory.decodeResource(getResources(), R.drawable.btn_shield), 24, 1));
		btnShield.setPosition(new PointF(visibleScreenArea.right - btnShield.getWidth() - 10 * SCALE, visibleScreenArea.bottom - btnShield.getHeight()));
		
		btnAttack = new Renderable(new Sprite(BitmapFactory.decodeResource(getResources(), R.drawable.btn_attack), 24, 1));
		btnAttack.setPosition(new PointF(btnShield.getPosition().x - btnShield.getWidth() - 10 * SCALE, visibleScreenArea.bottom - btnShield.getHeight()));
		
		scoreText.setColor(Color.BLACK);
		scoreText.setTextAlign(Align.CENTER);
		scoreText.setTextSize(14 * SCALE);
		Typeface celestia = Typeface.createFromAsset(getContext().getAssets(), RenderView.TYPEFACE_PATH);
		scoreText.setTypeface(celestia);
		scoreText.setAntiAlias(true);

		pauseText = new Paint(scoreText);
		pauseText.setTextSize(36 * SCALE);
		pauseText.setColor(0xCCFEC000);
		scoreBoardFill.setColor(0xCCFFFFFF);
		scoreBoardFill.setStyle(Style.FILL);
		scoreBoardStroke.setColor(0xCCFEC000);
		scoreBoardStroke.setStyle(Style.STROKE);
		scoreBoardStroke.setStrokeWidth(5);

		// Set game end time
		gameStartTime = SystemClock.elapsedRealtime() + countdownLeft * 1000;
		nextFlashTime = (long) (gameStartTime + Math.random() * 5000);
		
		this.initialized = true;
	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		Log.i(getClass().getName(), "surfaceCreated - initialized:" + initialized);
		if (initialized)
			return;
		this.initialize();
		// Start render system
		this.renderThread.startRender();
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		if (this.initialized == false && gameRunning == false) {
			boolean retry = true;
			this.renderThread.stopRender();
			while (retry) {
				try {
					this.renderThread.join();
					retry = false;
				} catch (InterruptedException e) {
					// we will try it again and again...
				}
			}
			this.initialized = false;
		}
	}

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
	}

	public void pauseGame() {
		gamePauseTime = SystemClock.elapsedRealtime();
		gamePaused = true;
		this.renderThread.pauseRender(true);
	}

	public void resumeGame() {
		long timeDiff = SystemClock.elapsedRealtime() - gamePauseTime;
		gameRunTime -= timeDiff;
		gamePaused = false;
		this.renderThread.resumeRender();
	}

	public void onAttackButtonClick(boolean pressed){
		if(pressed)
			player.attack(enemy);
	}
	
	public void onShieldButtonClick(boolean pressed){
		this.player.defence = pressed;
	}
	
	public void destroy() {
		this.initialized = false;
	}

	public void render(long gameTime) {
		Canvas c = null;
		try {
			c = surfaceHolder.lockCanvas(null);
			synchronized (surfaceHolder) {
				this.update(gameTime);
				this.drawFrame(c);
			}
		} finally {
			if (c != null) {
				surfaceHolder.unlockCanvasAndPost(c);
			}
		}
	}

	private void update(long gameTime) {
		gameRunTime = (gameTime - gameStartTime) / 1000;
		if (gamePaused == true)
			return;
		if (gameTime >= gameStartTime) {
			gameRunning = true;
		} else {
			countdownLeft = (int) ((gameStartTime - gameTime) / 1000);
		}
		if (gameRunning == false)
			return;
		
		if (player.hp <= 0) {
			this.gameRunning = false;
			this.renderThread.stopRender();
			this.gameOverListener.gameOver(gameRunTime, SCORE);
			this.destroy();
			return;
		}
		
		this.cloudTop.update(gameTime);
		this.cloudBottom.update(gameTime);
		
		this.player.update(gameTime);
		this.enemy.update(gameTime);
		this.projectileCollector.update(gameTime);
		
		if(this.projectile.firing == false && this.enemy.canFire()){
			this.projectile.fire();
			this.enemy.clearCanFire();
		}
		
		this.projectile.update(gameTime);
		if(this.enemy.hp <= 0){
			this.enemy.spawn();
			SCORE++;
		}
		
		if(gameTime > nextFlashTime && flash == false){
			flash = true;
			nextFlashTime = (long) (gameTime + Math.random() * 8000);
		}
		
	}

	protected void drawFrame() {
		Canvas c = null;
		try {
			c = surfaceHolder.lockCanvas(null);
			synchronized (surfaceHolder) {
				this.drawFrame(c);
			}
		} finally {
			if (c != null) {
				surfaceHolder.unlockCanvasAndPost(c);
			}
		}
	}

	protected void drawFrame(Canvas canvas) {
		// Can't draw if not initialized
		if(this.initialized == false) return;
		// Draw Background
		drawBackground(canvas);
		// Draw game ojects
		this.player.draw(canvas);
		this.projectileCollector.draw(canvas);
		this.enemy.draw(canvas);
		this.projectile.draw(canvas);
		// Draw score
		this.drawScore(canvas);
		
		this.btnShield.draw(canvas);
		this.btnAttack.draw(canvas);
		
		// Draw Pause screen
		if (gamePaused == true) {
			int y = (int) (visibleScreenArea.height() / 2 - pauseText.getTextSize() / 2);
			canvas.drawText(getString(R.string.pause_pause),
					visibleScreenArea.width() / 2, y, pauseText);
			canvas.drawText(getString(R.string.pause_tabanywhere),
					visibleScreenArea.width() / 2, (float) (y + pauseText.getTextSize() / 2), scoreText);
		}
		

	}

	private void drawBackground(Canvas canvas) {
		if(flash){
			canvas.drawColor(0xffffffff);
			flash = false;
		}else{
			canvas.drawColor(backgroundColor);
		}
		if(player.wasHit){
			canvas.drawColor(Color.RED);
			player.wasHit = false;
		}
		this.cloudTop.draw(canvas);
		this.cloudBottom.draw(canvas);
	}

	private void drawScore(Canvas canvas) {
		RectF scoreRectF = new RectF(0, -25, visibleScreenArea.right, (float) (scoreText.getTextSize() * 1.4));
		canvas.drawRoundRect(scoreRectF, 20, 20, scoreBoardFill);
		canvas.drawRoundRect(scoreRectF, 20, 20, scoreBoardStroke);
		canvas.drawText("Health: " + player.hp + " | Shield: " + player.charge + " | Enemy: " + enemy.hp + " | Time: " + gameRunTime + "s | Defeated: " + SCORE,
							visibleScreenArea.width() / 2,
							scoreText.getTextSize(), scoreText);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		// Can't interact if not initialized
		if(this.initialized == false) return super.onTouchEvent(event);

		int action = event.getAction();
		int actionCode = action & MotionEvent.ACTION_MASK;
		if(actionCode == MotionEvent.ACTION_POINTER_DOWN){
			actionCode = MotionEvent.ACTION_DOWN;
		}
		if(actionCode == MotionEvent.ACTION_POINTER_UP){
			actionCode = MotionEvent.ACTION_UP;
		}
		int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;
		handleTouchEvent(actionCode, event.getX(pointerIndex), event.getY(pointerIndex));

		return true;		
	}

	public void handleTouchEvent(int action, float x, float y){		
		if(action == MotionEvent.ACTION_DOWN && this.gamePaused){
			this.resumeGame();
		}
		// left half
		if (x <= visibleScreenArea.centerX()) {		
			if(action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE)
				player.setTargetY(y - (player.getSprite().getHeight() / 2));
		} else {
			if(action == MotionEvent.ACTION_DOWN){
				if(btnShield.isHit(x, y)){
					this.onShieldButtonClick(true);					
				}
				if (btnAttack.isHit(x, y)){
					this.onAttackButtonClick(true);
				}
			}
			if(action == MotionEvent.ACTION_UP){
				this.onShieldButtonClick(false);	
				this.onAttackButtonClick(false);
			}
		}		
	}
	
	Runnable longPressKeyHanlderUp = new Runnable() {		
		@Override
		public void run() {
			player.moveUp();
			postDelayed(this, 40);			
		}
	};
	
	Runnable longPressKeyHanlderDown = new Runnable() {		
		@Override
		public void run() {
			player.moveDown();
			postDelayed(this, 40);			
		}
	};
	
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		switch (keyCode) {
			case KeyEvent.KEYCODE_DPAD_UP:
				longPressKeyHanlderUp.run();
				break;
			case KeyEvent.KEYCODE_DPAD_DOWN:
				longPressKeyHanlderDown.run();
				break;
			case KeyEvent.KEYCODE_DPAD_CENTER:
				this.onAttackButtonClick(true);
				break;
			case KeyEvent.KEYCODE_BACK:
				if(event.isAltPressed()){
					this.onShieldButtonClick(true);				
				} else {
					return false;
				}
				break;
			case KeyEvent_KEYCODE_BUTTON_START:
				if(this.gamePaused){
					this.resumeGame();
				} else {
					this.pauseGame();
				}
			default:
				return false;
		}
		return true;
	}
	
	@Override
	public boolean onKeyUp(int keyCode, KeyEvent event) {
		switch (keyCode) {
			case KeyEvent.KEYCODE_DPAD_UP:
				removeCallbacks(longPressKeyHanlderUp);
				break;
			case KeyEvent.KEYCODE_DPAD_DOWN:
				removeCallbacks(longPressKeyHanlderDown);
				break;
			case KeyEvent.KEYCODE_DPAD_CENTER:
				this.onAttackButtonClick(false);
				break;
			case KeyEvent.KEYCODE_BACK:
				if(event.isAltPressed()){
					this.onShieldButtonClick(false);				
				} else {
					return false;
				}
				break;
			default:
				return false;
		}
		return true;
	}
	
	public void setGameOverListener(GameOverListener gameOverListener) {
		this.gameOverListener = gameOverListener;
	}

	public GameOverListener getGameOverListener() {
		return gameOverListener;
	}

	private String getString(int resId) {
		return getContext().getString(resId);
	}

	@SuppressWarnings("unused")
	private String getString(int resId, Object... formatArgs) {
		return getContext().getString(resId, formatArgs);
	}
}
